package com.ehome.poct.core.service.pay;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.ehome.paymentcenter.api.account.enums.UserTypeEnum;
import com.ehome.paymentcenter.api.pay.BasePayReqCO;
import com.ehome.paymentcenter.api.pay.BaseRspCO;
import com.ehome.paymentcenter.api.pay.domain.PayExtend;
import com.ehome.paymentcenter.api.pay.domain.PsAmountInfo;
import com.ehome.poct.core.api.constant.enums.ScanTypeEnums;
import com.ehome.poct.core.api.dto.order.TestOrderItemDTO;
import com.ehome.poct.core.api.dto.order.TestPoctProfitsRecordDTO;
import com.ehome.poct.core.api.dto.order.UnificationPayReqDTO;
import com.ehome.poct.core.api.virtual.TestOrderService;
import com.ehome.poct.core.api.vo.order.PayChannelRespVO;
import com.ehome.poct.core.constant.OrderBizCodeEnums;
import com.ehome.poct.core.mapping.TestOrderMapping;
import com.ehome.poct.core.repository.order.TestOrderItemRepository;
import com.ehome.poct.core.repository.order.TestOrderRepository;
import com.ehome.poct.core.repository.order.TestOrderUnificationPayRepository;
import com.ehome.poct.core.service.mapping.TestOrderUnificationPayMapping;
import com.ehome.poct.core.service.rpc.CorePayClient;
import com.ehome.poct.core.utils.EHomeAssert;
import com.google.common.collect.Lists;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @auther: kuiba.peng
 * @date: 2022/10/28 11:53
 * @description:
 */
@Getter
@Slf4j
@Component
public abstract class AbstractPayStrategy<T extends BasePayReqCO, R extends BaseRspCO> {

    public static Integer BUSINESS_SUCCESS_CODE = 0;

    @Resource
    private CorePayClient payClient;

    @Resource
    TestOrderUnificationPayMapping unificationPayMapping;

    @Resource
    TestOrderUnificationPayRepository unificationPayRepository;

    @Resource
    TestOrderRepository orderRepository;

    @Resource
    TestOrderItemRepository orderItemRepository;

    @Resource
    TestOrderMapping orderMapping;

    /**
     * 发起支付请求
     *
     * @param payReqDTO 支付参数
     * @return
     */
    public abstract PayChannelRespVO<R> pay(T payReqDTO);

    public abstract PayChannelRespVO<R> pay(UnificationPayReqDTO payReqDTO);

    public abstract ScanTypeEnums getSupportType();

    /**
     * 校验支付方式
     *
     * @param payReqDTO
     * @return
     */
    public abstract boolean supports(Object payReqDTO);

    /**
     * 获取分账信息
     *
     * @param shareAccountList
     * @return
     */
    private List<PsAmountInfo> getAmountInfoList(List<TestPoctProfitsRecordDTO> shareAccountList) {
        if (CollUtil.isEmpty(shareAccountList)) {
            return Lists.newArrayList();
        }
        List<PsAmountInfo> amountInfoList = new ArrayList<>(shareAccountList.size() * 4);
        shareAccountList.forEach(shareAccount -> {
            addShareAmountInfo(amountInfoList, shareAccount.getClinicId(), shareAccount.getClinicProfits(), UserTypeEnum.CLINIC, shareAccount.getStripName());
            addShareAmountInfo(amountInfoList, shareAccount.getCityAgentId(), shareAccount.getCityAgentProfits(), UserTypeEnum.AGENT, shareAccount.getStripName());
            addShareAmountInfo(amountInfoList, shareAccount.getProvAgentId(), shareAccount.getProvAgentProfits(), UserTypeEnum.AGENT, shareAccount.getStripName());
            addShareAmountInfo(amountInfoList, shareAccount.getCompanyAgentId(), shareAccount.getCompanyAgentProfits(), UserTypeEnum.AGENT, shareAccount.getStripName());
            addShareAmountInfo(amountInfoList, shareAccount.getSalesId(), shareAccount.getSalesProfits(), UserTypeEnum.AGENT, shareAccount.getStripName());
        });
        return amountInfoList;
    }

    /**
     * 获取单个分账信息
     *
     * @param amountInfoList
     * @param shareUserId       分账方id
     * @param shareAccountTotal 分账金额
     * @param userType          用户类型
     * @param stripName         检测项名称
     */
    private void addShareAmountInfo(List<PsAmountInfo> amountInfoList
            , Long shareUserId
            , BigDecimal shareAccountTotal
            , UserTypeEnum userType
            , String stripName) {
        PsAmountInfo psAmountInfo = addShareAmountInfo(shareUserId, shareAccountTotal, userType, stripName);
        if (Objects.isNull(psAmountInfo)) {
            return;
        }
        amountInfoList.add(psAmountInfo);
    }

    private PsAmountInfo addShareAmountInfo(Long clinicId, BigDecimal shareAccountTotal, UserTypeEnum userType, String stripName) {
        if (Objects.isNull(shareAccountTotal) || shareAccountTotal.compareTo(BigDecimal.ZERO) <= 0) {
            return null;
        }
        return new PsAmountInfo().setUserId(clinicId)
                .setAmount(shareAccountTotal.multiply(BigDecimal.valueOf(100)).longValue())
                .setUserType(userType).setContent(stripName);
    }

    /**
     * 获取拓展信息
     *
     * @param orderItemList
     * @param profitsRecordList
     * @param payMoney
     * @return
     */
    public PayExtend getPayExtend(List<TestOrderItemDTO> orderItemList, List<TestPoctProfitsRecordDTO> profitsRecordList, Long payMoney) {
        List<PsAmountInfo> amountInfoList = getAmountInfoList(profitsRecordList);
        PayExtend payExtend = new PayExtend();
        payExtend.setPsAmountInfos(amountInfoList);

        Long shareAmountTotal = amountInfoList.stream().map(PsAmountInfo::getAmount).filter(Objects::nonNull).reduce(0L, Long::sum);
        EHomeAssert.notNull(payMoney, OrderBizCodeEnums.PAY_AMOUNT_NO_IS_NULL);
        EHomeAssert.isTrue(shareAmountTotal <= payMoney, OrderBizCodeEnums.SHARE_ACCOUNT_AMOUNT_FAIL);

        EHomeAssert.isTrue(CollUtil.isNotEmpty(orderItemList), OrderBizCodeEnums.ORDER_ITEM_NO_IS_NULL);

        // 循环依赖
        final var itemPrices = SpringUtil.getBean(TestOrderService.class).getPriceItemByOrderItem(orderItemList);
        final var amountDetailList = orderMapping.to(itemPrices);
        payExtend.setAmountDetailList(amountDetailList);
        return payExtend;
    }
}
